package com.github.wxiaoqi.security.gate.filter;

import java.io.IOException;
import java.net.URLEncoder;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;

import com.alibaba.fastjson.JSON;
import com.github.wxiaoqi.security.api.vo.authority.PermissionInfo;
import com.github.wxiaoqi.security.api.vo.log.LogInfo;
import com.github.wxiaoqi.security.auth.client.jwt.ServiceAuthUtil;
import com.github.wxiaoqi.security.auth.client.jwt.UserAuthUtil;
import com.github.wxiaoqi.security.auth.common.config.ServiceAuthConfig;
import com.github.wxiaoqi.security.auth.common.config.UserAuthConfig;
import com.github.wxiaoqi.security.auth.common.util.jwt.IJWTInfo;
import com.github.wxiaoqi.security.common.admin.pay.response.BaseResponse;
import com.github.wxiaoqi.security.common.context.BaseContextHandler;
import com.github.wxiaoqi.security.common.msg.ResponseCode;
import com.github.wxiaoqi.security.common.util.ClientUtil;
import com.github.wxiaoqi.security.common.util.EntityUtils;
import com.github.wxiaoqi.security.common.util.FilterUtil;
import com.github.wxiaoqi.security.common.util.MD5;
import com.github.wxiaoqi.security.common.util.ValidateSign;
import com.github.wxiaoqi.security.gate.feign.ILogService;
import com.github.wxiaoqi.security.gate.feign.IUserService;
import com.github.wxiaoqi.security.gate.utils.DBLog;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.http.HttpServletRequestWrapper;
import com.netflix.zuul.http.ServletInputStreamWrapper;

import lombok.extern.slf4j.Slf4j;
/**
 * ${DESCRIPTION}
 *
 */
@Component
@Slf4j
public class AdminAccessFilter extends ZuulFilter {
	@Autowired
	@Lazy
	private IUserService userService;
	@Autowired
	@Lazy
	private ILogService logService;

	@Value("${gate.ignore.startWiths}")
	private String startWith;

	@Value("${zuul.prefix}")
	private String zuulPrefix;
	@Autowired
	private UserAuthUtil userAuthUtil;

	@Autowired
	private ServiceAuthConfig serviceAuthConfig;

	@Autowired
	private UserAuthConfig userAuthConfig;

	@Autowired
	private ServiceAuthUtil serviceAuthUtil;

	/**
	 * crmApi拦截地址 
	 * */
	@Value("${gate.crm.startWith}")
	private String crmApiStartWith;
	/**
	 * bizApi拦截地址 
	 * */
	@Value("${gate.biz.startWith}")
	private String bizApiStartWith;
	@Autowired
	private RedisTemplate redisTemplate;
	@Override
	public String filterType() {
		return "pre";
	}

	@Override
	public int filterOrder() {
		return 1;
	}

	@Override
	public boolean shouldFilter() {
		return true;
	}

	@Override
	public Object run() {
		RequestContext ctx = RequestContext.getCurrentContext();
		HttpServletRequest request = ctx.getRequest();
		final String requestUri = request.getRequestURI().substring(zuulPrefix.length());
		final String method = request.getMethod();
		/*if(!checkIpList(getIpAdrress(request))){
			setFailedRequest(ResponseCode.IPLIST_CHECKFAIL_CODE.getCode(),ResponseCode.IPLIST_CHECKFAIL_CODE.getMessage());
			return null;
		}*/
		BaseContextHandler.setToken(null);
		// 不进行拦截的地址
		if (isStartWith(requestUri)) {
			return null;
		}
		IJWTInfo user = null;
		try {
			user = getJWTUser(request, ctx);
			if(null==user){
				log.info("获取授权token报错：获取的token是null");
				setFailedRequest(ResponseCode.TOKEN_ISNULL_CODE.getCode(),ResponseCode.TOKEN_ISNULL_CODE.getMessage());
				return null;
			}
		} catch (Exception e) {
			log.info("获取授权token报错："+e.getMessage());
			setFailedRequest(ResponseCode.TOKEN_ISNULL_CODE.getCode(),ResponseCode.TOKEN_ISNULL_CODE.getMessage());
			return null;
		}
		//如果上传的是文件单独取参数
//		if (!ServletFileUpload.isMultipartContent(request)){
//			///admin/user/front/menu/all
//			if(!requestUri.equals("/admin/user/front/info")&&!requestUri.equals("/admin/user/front/menus")&&!requestUri.equals("/admin/user/front/menu/all")){
//				String requstParamsStr=EntityUtils.getBodyData(request);
//				Object platformId = null;
//				Object userType = null;
//				Map<String, Object>  requstParamsMap = new HashMap<String, Object>();
//				if(StringUtils.isEmpty(requstParamsStr)){
//					//get请求
//					platformId = request.getParameter("platformId");
//					userType = request.getParameter("userType");
//				} else {
//					//post请求
//					requstParamsMap = JSON.parseObject(requstParamsStr);
//					platformId = requstParamsMap.get("platformId");
//					userType = requstParamsMap.get("userType");
//				}
//	
//				if(StringUtils.isEmpty(platformId)){
//					log.info("platformId 不能为空 ---");
//					setFailedRequest(ResponseCode.INVALID_FIELDS.getCode(),ResponseCode.INVALID_FIELDS.getMessage());
//					return null;
//				}
//				if(StringUtils.isEmpty(userType)){
//					log.info("userType 不能为空 ---");
//					setFailedRequest(ResponseCode.INVALID_FIELDS.getCode(),ResponseCode.INVALID_FIELDS.getMessage());
//					return null;
//				}
//				if(!StringUtils.isEmpty(platformId) && !StringUtils.isEmpty(userType)){
//					if(StringUtils.isEmpty(requstParamsStr)){
//						//get请求
//						if (platformId.equals("1001")) {
//							Map<String, List<String>> params = ctx.getRequestQueryParams();
//						    if (params == null) {
//						        params = Maps.newHashMap();
//						    }
//					        params.put("platformId", Lists.newArrayList(""));
//						    ctx.setRequestQueryParams(params);
//						}
//					}else{
//						//post请求
//						if (platformId.equals("1001")) {
//							requstParamsMap.put("platformId", null);
//						}
//					}
//				}
//				
//				if(!StringUtils.isEmpty(requstParamsStr)){
//					//post请求
//					final byte[] reqBodyBytes = JSON.toJSONString(requstParamsMap).getBytes();
//					ctx.setRequest(new HttpServletRequestWrapper(RequestContext.getCurrentContext().getRequest()) {
//						@Override
//						public ServletInputStream getInputStream() throws IOException {
//							return new ServletInputStreamWrapper(reqBodyBytes);
//						}
//						@Override
//						public int getContentLength() {
//							return reqBodyBytes.length;
//						}
//						@Override
//						public long getContentLengthLong() {
//							return reqBodyBytes.length;
//						}
//					});
//				}
//			}
//		}
		boolean istokenvalid=false;
		if(requestUri.startsWith(crmApiStartWith) || requestUri.startsWith(bizApiStartWith)){
			/**
			 * 验证token是否合法有效
			 * */
			try {
				istokenvalid=checkToken(user.getUniqueName(),request);
				if(!istokenvalid){
					log.info("验证比对Token数据不一致: redis缓存username ---"+user.getUniqueName());
					setFailedRequest(ResponseCode.TOKEN_CHECKFAIL_CODE.getCode(),ResponseCode.TOKEN_CHECKFAIL_CODE.getMessage());
					return null;
				}
			} catch (Exception e) {
				log.info("验证Token数据报错:"+e.getMessage());
				setFailedRequest(ResponseCode.TOKEN_CHECKFAIL_CODE.getCode(),ResponseCode.TOKEN_CHECKFAIL_CODE.getMessage());
				return null;
			}
			/***
			 * 校验签名
			 * */
			String requstParamsStr=EntityUtils.getBodyData(request);
			Map<String, Object>  requstParamsMap= JSON.parseObject(requstParamsStr);
			String sign= (String) requstParamsMap.get("sign");
			if(null==sign|| "".equals(sign)){
				log.info("网关解析传输参数错误:签名为空");
				setFailedRequest(ResponseCode.TOKEN_SIGNISNULL_CODE.getCode(),ResponseCode.TOKEN_SIGNISNULL_CODE.getMessage());
				return null;
			}
			requstParamsMap.remove("sign");
			if(!ValidateSign.checkSign(sign, user.getSaltKey(), requstParamsMap)){
				log.info("网关校验传输参数-签名失败:"+"--之前的签名:--"+sign+"---签名盐值--"+user.getSaltKey()+"---排序后的参数---"+FilterUtil.createLinkString(requstParamsMap));
				setFailedRequest(ResponseCode.TOKEN_CHECKSIGNFAIL_CODE.getCode(),ResponseCode.TOKEN_CHECKSIGNFAIL_CODE.getMessage());
				return null;
			}
			final byte[] reqBodyBytes = requstParamsStr.getBytes();
			ctx.setRequest(new HttpServletRequestWrapper(RequestContext.getCurrentContext().getRequest()) {
				@Override
				public ServletInputStream getInputStream() throws IOException {
					return new ServletInputStreamWrapper(reqBodyBytes);
				}
				@Override
				public int getContentLength() {
					return reqBodyBytes.length;
				}
				@Override
				public long getContentLengthLong() {
					return reqBodyBytes.length;
				}
			});
		}else{
			//查询出所有菜单权限
			List<PermissionInfo> permissionIfs = userService.getAllPermissionInfo();
			// 判断资源是否启用权限约束
			Stream<PermissionInfo> stream = getPermissionIfs(requestUri, method, permissionIfs);
			//把stream中的元素转换成List
			List<PermissionInfo> result = stream.collect(Collectors.toList());
			PermissionInfo[] permissions = result.toArray(new PermissionInfo[]{});
			//permissions.length > 0 说明请求路由需要授权 匹配上的请求路由需要判断是否给用户授权没有给返回未授权
			if (permissions.length > 0) {
				checkUserPermission(permissions, ctx, user);
			}
		}
		// 申请客户端密钥头
		// 获取微服务client授权token 以便请求其他服务时可以通过验证
		ctx.addZuulRequestHeader(serviceAuthConfig.getTokenHeader(), serviceAuthUtil.getClientToken());
		return null;
	}

	/**
	 * 获取目标权限资源
	 *
	 * @param requestUri
	 * @param method
	 * @param serviceInfo
	 * @return
	 */
	private Stream<PermissionInfo> getPermissionIfs(final String requestUri, final String method, List<PermissionInfo> serviceInfo) {
		return serviceInfo.parallelStream().filter(new Predicate<PermissionInfo>() {
			@Override
			public boolean test(PermissionInfo permissionInfo) {
				String url = permissionInfo.getUri();
				String uri = url.replaceAll("\\{\\*\\}", "[a-zA-Z\\\\d]+");
				String regEx = "^" + uri + "$";
				if (Pattern.compile(regEx).matcher(requestUri).find() || (requestUri.startsWith(url + "/"))
						&& method.equals(permissionInfo.getMethod())) {
					log.info("requestUri-------{}",requestUri + "=======" + regEx);
					log.info("uri正则匹配---------{}",Pattern.compile(regEx).matcher(requestUri).find());
					log.info("uri正则匹配---------{}",(requestUri.startsWith(url + "/")&& method.equals(permissionInfo.getMethod())));
				}
				return (Pattern.compile(regEx).matcher(requestUri).find() || (requestUri.startsWith(url + "/"))
						&& method.equals(permissionInfo.getMethod()));
			}
		});
	}
	
//	public static void main(String args[]){
//	      String content = "/admin/user/front/menus";
//	 
//	      String pattern = "^/admin$";
//	      System.out.println(Pattern.compile(pattern).matcher(content).find());
//	      boolean isMatch = Pattern.matches(pattern, content);
//	      System.out.println("字符串中是否包含了 'runoob' 子字符串? " + isMatch);
//	   }
	

	private void setCurrentUserInfoAndLog(RequestContext ctx, IJWTInfo user, PermissionInfo pm) {
		String host = ClientUtil.getClientIp(ctx.getRequest());
		ctx.addZuulRequestHeader("userId", user.getXId());
		ctx.addZuulRequestHeader("userName", URLEncoder.encode(user.getName()));
		ctx.addZuulRequestHeader("userHost", ClientUtil.getClientIp(ctx.getRequest()));
		LogInfo logInfo = new LogInfo(user.getPlatformId() ,pm.getMenu(), pm.getName(), pm.getUri(), new Date(), user.getXId(), user.getName(), host);
		DBLog.getInstance().setLogService(logService).offerQueue(logInfo);
	}

	/**
	 * 返回session中的用户信息
	 *
	 * @param request
	 * @param ctx
	 * @return
	 */
	private IJWTInfo getJWTUser(HttpServletRequest request, RequestContext ctx) throws Exception {
		String authToken = request.getHeader(userAuthConfig.getTokenHeader());
		/*if (StringUtils.isEmpty(authToken)) {
			authToken = request.getParameter("token");
		}*/
		ctx.addZuulRequestHeader(userAuthConfig.getTokenHeader(), authToken);
		BaseContextHandler.setToken(authToken);
		return userAuthUtil.getInfoFromToken(authToken);
	}


	private void checkUserPermission(PermissionInfo[] permissions, RequestContext ctx, IJWTInfo user) {
		//查询出登陆用户所具有的菜单权限
		List<PermissionInfo> permissionInfos = userService.getPermissionByUsername(user.getUniqueName());
		PermissionInfo current = null;
		for (PermissionInfo info : permissions) {
			boolean anyMatch = permissionInfos.parallelStream().anyMatch(new Predicate<PermissionInfo>() {
				@Override
				public boolean test(PermissionInfo permissionInfo) {
					return permissionInfo.getCode().equals(info.getCode());
				}
			});
			if (anyMatch) {
				current = info;
				break;
			}
		}
		//提示登陆用户暂无权限
		if (current == null) {
			setFailedRequest(ResponseCode.USER_NO_MENU_AUTH.getCode(),ResponseCode.USER_NO_MENU_AUTH.getMessage());
		} else {
			if (!RequestMethod.GET.toString().equals(current.getMethod())) {
				setCurrentUserInfoAndLog(ctx, user, current);
			}
		}
	}

	/**
	 * 验证当前应用的token是否正确:应用传参过来的token和缓存中的token比对
	 * @author
	 * */
	private boolean checkToken(String platformId,HttpServletRequest request)throws Exception{
		String authToken = request.getHeader(userAuthConfig.getTokenHeader());
		String cacheAuthToken=String.valueOf(redisTemplate.opsForValue().get("auth:token_"+platformId));
		if("".equals(cacheAuthToken)||null==cacheAuthToken){
			return false;
		}
		if(authToken.equals(cacheAuthToken)){
			return true;
		}
		return false;
	}

	/**
	 * URI是否以什么打头
	 *
	 * @param requestUri
	 * @return
	 */
	private boolean isStartWith(String requestUri) {
		boolean flag = false;
		for (String s : startWith.split(",")) {
			if (requestUri.startsWith(s)) {
				return true;
			}
		}
		return flag;
	}

	/**
	 * 网关抛异常
	 *
	 * @param body
	 * @param code
	 */
	private void setFailedRequest(String body, String code) {
		log.debug("Reporting error ({}): {}", code, body);
		BaseResponse baseResponse= new BaseResponse(code,body);
		RequestContext ctx = RequestContext.getCurrentContext();
		HttpServletResponse httpResponse=ctx.getResponse();
		httpResponse.setCharacterEncoding("utf-8");
		ctx.setResponseStatusCode(200);
		if (ctx.getResponseBody() == null) {
			ctx.setResponseBody(EntityUtils.BeanToJsonStr(baseResponse));
			ctx.setSendZuulResponse(false);
		}
	}
	
	/**
	 * 获取ip地址
	 * */
	 private  String getIpAdrress(HttpServletRequest request) {
	        String Xip = request.getHeader("X-Real-IP");
	        String XFor = request.getHeader("X-Forwarded-For");
	        if(StringUtils.isNotEmpty(XFor) && !"unKnown".equalsIgnoreCase(XFor)){
	            //多次反向代理后会有多个ip值，第一个ip才是真实ip
	            int index = XFor.indexOf(",");
	            if(index != -1){
	                return XFor.substring(0,index);
	            }else{
	                return XFor;
	            }
	        }
	        XFor = Xip;
	        if(StringUtils.isNotEmpty(XFor) && !"unKnown".equalsIgnoreCase(XFor)){
	            return XFor;
	        }
	        if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
	            XFor = request.getHeader("Proxy-Client-IP");
	        }
	        if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
	            XFor = request.getHeader("WL-Proxy-Client-IP");
	        }
	        if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
	            XFor = request.getHeader("HTTP_CLIENT_IP");
	        }
	        if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
	            XFor = request.getHeader("HTTP_X_FORWARDED_FOR");
	        }
	        if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
	            XFor = request.getRemoteAddr();
	        }
	        return XFor;
	    }
	 
	 /**
	  * 校验IP白名单
	  * */
	 public boolean  checkIpList(String realIp){
		 List<Map<String ,Object>> redisIplist=(List<Map<String, Object>>) redisTemplate.opsForValue().get("i_flyray-admin:ipList");
		 boolean isexist=false;
		 log.info("获取请求的真实的ip地址："+realIp+"---获取IP白名单数量："+redisIplist.size());
		 try {
			if(null!=redisIplist&&redisIplist.size()>0){
				 for(Map<String , Object>ipmap:redisIplist){
					 if(realIp.equals(ipmap.get("ip"))){
						 isexist=true;
					 }
				 }
			 }
		} catch (Exception e) {
			e.printStackTrace();
			log.info("校验IP白名单报错："+e.getMessage());
			return isexist;
		}
		return isexist;
	 }

}
